class BlackDeathEnemyController extends AIController;
var vector nextDestinationVector;
var BlackDeathPawn BDP;
var int MaxPerceptionRange, i;
var float r;
var Rotator rot;

var vector lastLoc;
var int stuckCount;

var float FrenzyDistance;//Distance to begin frenzying

var float FearRetreatDistance; //Distance that a bot wishs to retreat to when backing away from a fear;

var float RotationSpeed;//How fast the zombie should turn to face on on spotting you

var float FearLength; //Amount of time zombie will be afraid before auto-returning to normal

auto state Idle
{
//Plays when an enemy is not currently looking at anything 
Begin:
	//`log("Zombie idle");
	Enemy = None;
	if(BlackDeathZombiePawn(Pawn) != none)
	{
		BlackDeathZombiePawn(Pawn).changeState(0, 0.1);
	}
	While(!Search())//Continuous force the enemy to try and lock onto the player
	{
		//`log("I didn't see anyone");
		//play some idel animation stuff?
		r = RandRange(0.0, 10.0);
		//`log("R: "@r);
		if(r > 9)
		{
			//`log("Wondering");
			if(Wander())
			MoveTo(NextDestinationVector);
		}
		else
		{
			//`log("sleeping");
			sleep(1);
		}
	}
	goToState('Faceing');//We should chase him now
}

state Faceing
{
Begin:
	if(BlackDeathZombiePawn(Pawn) != none)
	{
		BlackDeathZombiePawn(Pawn).changeState(5, 0.4);
		BlackDeathZombiePawn(Pawn).Notice.PlayAnim(false,1.0, 0);
		
	}
	nextDestinationVector = Normal(Enemy.Location-Pawn.location);//The normalized direction to our enemy
	r = nextDestinationVector dot Vector(Pawn.rotation);
	rot = Pawn.Rotation;
	while(r < 0.9)
	{
		Pawn.SetDesiredRotation(Rotator(nextDestinationVector),,,RotationSpeed-r,);
		nextDestinationVector = Normal(Enemy.Location-Pawn.location);//The normalized direction to our enemy
		r = nextDestinationVector dot Vector(Pawn.rotation);
		sleep(0.1);
	}
	BlackDeathZombiePawn(Pawn).changeState(5, 0.4);
	//does not change animation
		//BlackDeathZombiePawn(Pawn).Notice.PlayAnim(false,1.0, 0);
	goToState('pursuing');
}

state pursuing
{
//Plays when an enemy is chaseing someone
Begin:
	if(BlackDeathZombiePawn(Pawn) != none)
	{
		BlackDeathZombiePawn(Pawn).changeState(1, 0.5);
	}
	//Build path
	//While we haven't reached the target, follow: add some code to allow enemies to know how to break down wall
	//When we reached him, go to next state
	//`log("zombie purseing");
	while(pawn != none && enemy != none)
	{	
		//`log("in purseing");
		if(GetEnemyInSight())
		{
			//`log("Sighted");
			if(vsize(Enemy.location-Pawn.location) > FrenzyDistance)
			{
				//`log("Purseing"$vsize(Enemy.location-Pawn.location)$"     "$FrenzyDistance);
				//CurrentTargetIsReachable = NavActorReachable(Enemy);
				//`log("Reachable"@NavActorReachable(Enemy));
				if(!NavActorReachable(Enemy))
				{
					//WorldInfo.Game.Broadcast(None, "Indirect Path");
					Focus = none;
					nextDestinationVector = Enemy.Location;
					MoveToward(Enemy);
					Sleep(0.1);				
				}
				else
				{
					Focus = Enemy;
					nextDestinationVector = Enemy.Location;
					MoveToward(Enemy);
				}
			}
			else
			{
				//WorldInfo.Game.Broadcast(None, "Destination reached");
				gotostate('Frenzied');
			}
		}
		else
		{
			goToState('GoingToEnemyLastLocation');
		}
	}
	gotostate('Idle');
}

state GoingToEnemyLastLocation
{
	Begin:
	if(BlackDeathZombiePawn(Pawn) != none)
	{
		BlackDeathZombiePawn(Pawn).changeState(1, 0.1);
	}
	/** ToDo: Find a way to check if the point is reachable by the pawn, NavActorReachable() fails. */
	`log("Zombie Lost sight, moving to last target");
	nextDestinationVector = Enemy.Location;
	lastLoc = Pawn.Location;
	StuckCount = 0;
	SetTimer(0.4, true, 'checkStuck');
	While(nextDestinationVector != Pawn.location)
	{
		//`log("Havent reached location");
		if(GetEnemyInSight())
		{
			ClearTimer('CheckStuck');
			goToState('pursuing');
		}
		MoveTo(nextDestinationVector);
		sleep(0.1);
	}
	ClearTimer('CheckStuck');
	GotoState('Idle');
}

state Frenzied
{
	event BeginState(Name PreviousStateName)
	{
		Pawn.AirSpeed=130.000000;
		Pawn.GroundSpeed=100.000000;
	}
	event EndState(Name NextStateName)
	{
		Pawn.AirSpeed=75.000000;
		Pawn.GroundSpeed=60.000000;
	}

Begin:
	if(BlackDeathZombiePawn(Pawn) != none)
	{
		BlackDeathZombiePawn(Pawn).changeState(4, 0.1);
	}
	`log("zombie frenzied");
	while(vsize(Enemy.location-Pawn.location) <= FrenzyDistance)
	{
		//`log("zombie frenzied");
		if(vsize(Enemy.location-Pawn.location) <= Pawn.Weapon.WeaponRange)
		{
			goToState('attacking');
		}
		MoveToward(Enemy);
		sleep(0.1);
	}
	gotostate('pursuing');

}

state attacking
{
//Plays when an enemy is attacking something
	event EndState(Name NextStateName)
	{
		StopFiring();
	}
	
Begin:
	//while we are within range, attack
	//if we lose him, go back to purseuing
	`log("Zombie attack");
	

	
	//BDP = BlackDeathPawn(Pawn);
	//`log("Atttacking:"$vsize(Pawn.Location-Enemy.Location)$"   "$Pawn.Weapon.WeaponRange);
	Pawn.BotFire(true);//fire all guns! Unlease HELL!!
	while(Enemy != none && (vsize(Pawn.Location-Enemy.Location) <= Pawn.Weapon.WeaponRange))//If we're within range
	{
		sleep(0.1);//wait for attack anim to end
	}
	if(enemy == none)
	{
		gotostate('idle');
	}
	gotostate('Frenzied');

}

state burning
{
	begin:
	//`log("I. AM. ON. FIRE.");
	while(BlackDeathPawn(Pawn).bOnFire)
	{
		if(Wander())
		{
			MoveTo(NextDestinationVector);
		}
		sleep(0.1);
	}
	goToState('idle');
}

state Incapped
{
begin:

//`log("Incapped");
	MoveTo(Pawn.Location);
	while(BlackDeathZombiePawn(Pawn).bIncapped || BlackDeathZombiePawn(Pawn).bRevive)
	{
		//`log(BlackDeathZombiePawn(Pawn).bIncapped@" "@BlackDeathZombiePawn(Pawn).bRevive);
		sleep(1.0);
	}
	goToState('idle');
}

state Staggered
{
	Begin:
	if(BlackDeathZombiePawn(Pawn) != none)
	{
		BlackDeathZombiePawn(Pawn).changeState(2, 0.1);
	}
	//`log("Zombied staggered");
	nextDestinationVector = normal(Pawn.location - enemy.location);
	while(Pawn.isinState('Staggered'))
	{
		//`log("Zombie still staggering... ugghhhgg");
		MoveTo(Location+nextDestinationVector, Enemy);
		sleep(0.1);
	}
	MoveTo(Pawn.location);
	goTostate('pursuing');
}

state afraid
{
	event BeginState(Name PreviousStateName)
	{
		SetTimer(FearLength, false, 'finishFear');
	}
	event EndState(Name NextStateName)
	{
		ClearTimer('finishFear');
	}
	Begin:
	if(BlackDeathZombiePawn(Pawn) != none)
	{
		BlackDeathZombiePawn(Pawn).changeState(0, 0.1);
	}
	//`log("I are teh afraids of fire");
	//`log(Vsize(BlackDeathZombiePawn(Pawn).FearObject.Location-Pawn.location));
	While(Vsize(BlackDeathZombiePawn(Pawn).FearObject.Location-Pawn.location) < FearRetreatDistance)
	{

		nextDestinationVector = normal(Pawn.location - BlackDeathZombiePawn(Pawn).FearObject.Location);
		MoveTo(Location-nextDestinationVector, BlackDeathZombiePawn(Pawn).FearObject);
		sleep(0.1);
	}
	if(enemy != none)
	{
		nextDestinationVector = normal(Pawn.location - enemy.location);
		//`log(rotator(normal(nextDestinationVector)));
		rot = rotator(normal(nextDestinationVector));
		if(frand()>0.5)
		{
			rot.yaw+= 15000;

		}
		else
		{
			rot.yaw+= 15000;
		}
		MoveTo(Location-Vector(rot)*10, enemy);
	}
	gotostate('idle');
}

state damaged
{
	Begin:
	`log("Entering damaged state");
	//MoveTo(Location);
	while(BlackDeathZombiePawn(Pawn).bPlayingdamage)
	{
		sleep(0.1);
	}
	gotostate('idle');
}


simulated function InitNavigationHandle()
{
	if( NavigationHandleClass != None && NavigationHandle == none )
		NavigationHandle = new(self) NavigationHandleClass;
}

function bool NavActorReachable(Actor a)
{
	local bool retValue;

	if ( NavigationHandle == None )
		InitNavigationHandle();

	retValue = NavigationHandle.ActorReachable(a);
	
	//if(retValue)
	//	ClearTimer('NoPathDeathTimer');

	return retValue;
}

function bool Search()
{
	//get all the heros within your perception range
	local Vector V, PawnV;
	local float dot, dist;
	ForEach CollidingActors(class'BlackDeathPawn', BDP, MaxPerceptionRange, Pawn.Location)
	{
		if(BDP != Pawn)//ignore your own pawn
		{
			if(BlackDeathHeroPawn(BDP) != none && BDP.Health >0 && GetEnemyInSight(BDP))//if its a hero, which is an enemy
			{
				V = BDP.location;//get the new pawn's location
				V.Z = 0;//ignore z for now
				PawnV = Pawn.location;//our location
				PawnV.Z = 0;//ignore Z again
				Dot = Normal(V-PawnV) dot Vector(Pawn.rotation);//Take the direction of the line from me to you and dot it with my rotation (returns a number between -1 and 1, 1 for faceing dead on, -1 for dead away and 0 for a 90 degree angle).
				Dist = VSize(V-PawnV);//The distance between the pawns
				//`log(dot);
				//`log(dist);
				if(Dot > 0.5&&Dist <= 300)//if within a 90 degree cone of vision
				{
					enemy = BDP;
					return true;
				}
				if(Dot > 0&&Dist <= 200)//within a 180 degree view cone
				{
					enemy = BDP;
					return true;
				}
				if(Dist <= 100)//if behind
				{
					enemy = BDP;
					return true;
				}
			}
		}
	}
	return false;
	
}

function bool GetEnemyInSight(optional Pawn Target)
{
	//Determines if the enemy is in LOS.
	local Vector MyLoc, EnemyLoc;
	if(Target == none)
	{
		if(Enemy != none)
		{
			EnemyLoc = Enemy.location;
			EnemyLoc.z += Enemy.EyeHeight;
		}
		else
		{
			return false;
		}
	}
	else
	{
			EnemyLoc = Target.location;
			EnemyLoc.z += Target.EyeHeight;
	}
	MyLoc = Pawn.Location;
	MyLoc.Z += Pawn.EyeHeight;
	return fastTrace(EnemyLoc, MyLoc);
}

function bool ForceTargetPlayer()//Force the AI to target the player, useful for when we don't want to wait for a zombie to spot Vincient
{
	//Returns true if a target was found, else false
	local BlackDeathHeroPawn Player;
	foreach WorldInfo.AllPawns(class'BlackDeathHeroPawn',Player)
	{
		if(Player != none && Player.Controller != none)
		{
			if(Player.Controller.bIsPlayer)
			{
				enemy = Player;
				return true;
			}
		}
	}
	enemy = none;
	//`warn("no forse target player was found!");
	return false;
}

function bool Wander()
{
	//Randomly move somewhere
	local Vector V;
	if ( NavigationHandle == None )
	{
		InitNavigationHandle();
	}
	V = Pawn.location;
	V.x += RandRange(-50, 50.0);
	V.y += RandRange(-50.0, 50.0);
	if(NavigationHandle.PointReachable(V))
	{
		nextDestinationVector = V;
		return true;
	}
	return false;
}

function CheckStuck()
{
	`log("Checking stuck"@StuckCount);
	if(Pawn.location == LastLoc)
	{
		StuckCount += 1;
	}
	else
	{
		StuckCount = 0;
	}
	LastLoc = Pawn.location;
	if(StuckCount > 3)
	{
		goToState('idle');
		ClearTimer('CheckStuck');
	}
}

function CancelTarget()
{
	//Clear the enemy
	enemy = none;
}

function finishFear()
{
	goToState('idle');
}

DefaultProperties
{
	bUsingPathLanes=true
	MaxPerceptionRange = 1000
	bIsPlayer=true
	
	FearRetreatDistance = 180
	FearLength = 1.0
	
	RotationSpeed = 1.0
	FrenzyDistance = 150;
}